package cn.bellychang.arraylist.liuxin;

import cn.bellychang.iiterface.liuxin.Iterator;
import cn.bellychang.iiterface.liuxin.List;

/**
 * @author: bellychang
 * @date: 2020/2/6
 */
public class ArrayList<E> implements List<E> {

    public static final int MIN_CAPACITY = 1;
    public static final int MAX_CAPACITY = 20;
    public static final int INIT_CAPACITY = 2;
    private Object[] array;

    private int size;

    public ArrayList(int capacity) {
        // 1 1.5 = 1
        if (capacity <= MIN_CAPACITY || capacity > MAX_CAPACITY) {
            capacity = INIT_CAPACITY;
        }
        this.array = new Object[capacity];
    }

    @Override
    public void addLast(E e) {
        // check if array can allocate for this element
        rangeCheck();
        array[size++] = e;
    }

    private void rangeCheck() {
        if (size >= array.length) {
            grow();
        }
    }

    private void grow() {
        // expansion to origin's 1.5 times
        Object[] target = new Object[array.length + (array.length >> 1)];
        System.err.printf("extend capacity: before:[%d], after:[%d]\n", array.length, array.length + (array.length >> 1));
        System.arraycopy(array, 0, target, 0, size);
        array = target;
    }

    @Override
    public void add(int index, E e) {
        rangeCheck();
        // if index == getSize, do not need to do array copy
        if (index != getSize()) {
            // check if have an element in the index position
            System.arraycopy(array, index, array, index + 1, size - index);
            System.err.printf("array copy from [%d] to [%d], length=[%d]", index, index + 1, size - index);
        }
        array[index] = e;
        size++;
    }

    @Override
    public E get(int index) {
        checkIndex(index);
        return (E) array[index];
    }

    private void checkIndex(int index) {
        if (index > (size - 1) || index < 0) {
            throw new IllegalArgumentException();
        }
    }

    @Override
    public E remove(int index) {
        checkIndex(index);
        if (array[index] == null) {
            return null;
        }
        E temp = (E) array[index];
        if (size - index - 1 != 0) {
            System.arraycopy(array, index + 1, array, index, size - index - 1);
            System.err.printf("array copy from [%d] to [%d], length=[%d]", index + 1, index, size - index - 1);
        }
        array[size - 1] = null;
        size--;
        return temp;
    }

    @Override
    public int getSize() {
        return size;
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < size; i++) {
            builder.append(array[i]).append("->");
        }
        return builder.length() > 2 ? builder.toString().substring(0, builder.length() - 2) : builder.toString();
    }

    public Iterator<E> iterator() {
        return new ArrayListIterator();
    }

    /**
     * question: when using nested static class and inner class?
     * 为什么Java内部类要设计成静态和非静态两种 {@link:https://www.zhihu.com/question/28197253}
     */
    public class ArrayListIterator<E> implements Iterator<E> {

        private int position = 0;

        @Override
        public boolean hasNext() {
            return position <= (size - 1);
        }

        @Override
        public E next() {
            return (E) array[position++];
        }
    }

}

